Skip to content

Adds installation tests for conda/uv/modularized installation#5563

Draft
kellyguo11 wants to merge 6 commits intoisaac-sim:developfrom
kellyguo11:kellyg/install-tests-usd-core-25-11
Draft

Adds installation tests for conda/uv/modularized installation#5563
kellyguo11 wants to merge 6 commits intoisaac-sim:developfrom
kellyguo11:kellyg/install-tests-usd-core-25-11

Conversation

@kellyguo11
Copy link
Copy Markdown
Contributor

@kellyguo11 kellyguo11 commented May 10, 2026

Description

Adds automated tests for several installation paths, including using conda and uv environments and minimal installation of modularized packages.

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Checklist

  • I have read and understood the contribution guidelines
  • I have run the pre-commit checks with ./isaaclab.sh --format
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the changelog and the corresponding version in the extension's config/extension.toml file
  • I have added my name to the CONTRIBUTORS.md or my name already exists there

kellyguo11 and others added 3 commits May 9, 2026 22:38
Align the kitless OpenUSD dependency with Kit 110.1.1 so installs resolve the matching USD runtime.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add coverage for training workflow installs and conda-based install CI so the installation paths are exercised separately from the usd-core dependency bump.

Co-authored-by: Cursor <cursoragent@cursor.com>
@kellyguo11 kellyguo11 marked this pull request as draft May 10, 2026 06:04
@github-actions github-actions Bot added isaac-lab Related to Isaac Lab team infrastructure labels May 10, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 10, 2026

Greptile Summary

This PR adds automated CI for conda and uv installation paths, introduces a new Dockerfile.installci-conda layered on the existing uv image, and makes SimulationCfg/TerrainImporterCfg work without isaaclab_physx by widening the physics_material field type to RigidBodyMaterialBaseCfg.

  • New installation CI jobs: install-tests-conda runs in parallel with the existing uv job; both jobs now always run on every trigger (the path-gating if: condition was removed), and each restricts test selection via -m conda/-m uv markers to avoid redundant cross-runs.
  • install.py Isaac Sim overrides: Adds env-var control over version spec, extras, --pre, and extra index URLs (including auto-injection of internal NVIDIA Artifactory registries).
  • Modular import guards in cartpole_env_cfg.py: OvPhysxCfg and PhysxCfg are now soft-optional imports so the env config loads in kitless environments; the _preset_fields() helper in the preset system already supports post-decorator class attribute assignment via vars(cls) scanning.

Confidence Score: 3/5

The conda CI job's outer timeout equals its inner Docker run timeout, meaning build overhead will predictably cut the Docker run short on every run.

The conda job sets timeout-minutes: 120 while run_install_ci.py sets a Docker run timeout of 7200 s (also 120 min). Checkout, building the uv base image, and then the conda image on top will consume several minutes of that budget before the Docker container even starts, so the GitHub Actions runner will cancel the job before the tests finish — producing spurious failures on every slow-ish build.

.github/workflows/install-ci.yml — the conda job timeout and the dead any_match() function. source/isaaclab/isaaclab/cli/commands/install.py — the insert(0, url) ordering issue for internal registry URLs.

Important Files Changed

Filename Overview
.github/workflows/install-ci.yml Tests now always run unconditionally; any_match() function is dead code; conda job timeout-minutes equals the Docker run timeout leaving no headroom for build overhead.
docker/Dockerfile.installci-conda New Dockerfile layering Miniconda on the uv base image; uses MINICONDA_VERSION=latest (non-reproducible) and BASE_IMAGE build-arg will be passed by the runner but is not declared in this file.
source/isaaclab/isaaclab/cli/commands/install.py Adds env-var overrides for Isaac Sim version, extras, pre-release flag, and extra index URLs; insert(0, url) loop reverses the intended order of the two internal registry URLs.
tools/run_install_ci.py Adds --conda flag and builds the conda image on top of the uv image; unconditionally passes BASE_IMAGE build-arg to the conda Dockerfile which does not declare it, producing a Docker warning.
source/isaaclab/test/install_ci/test_install_workflow_training.py New training smoke tests for uv, conda, and _isaac_sim source paths; isaacsim_source-only tests correctly self-skip when the symlink is absent.
source/isaaclab/test/install_ci/utils.py Adds Conda_Mixin and IsaacSimSource_Mixin helpers; drop_keys cleanly strips venv markers; streaming mode correctly merges stderr into stdout.
source/isaaclab_tasks/isaaclab_tasks/direct/cartpole/cartpole_env_cfg.py Optional physx/ovphysx imports now guarded with try/except; post-class attribute assignment is supported by _preset_fields() which also scans vars(cls).
source/isaaclab/isaaclab/sim/simulation_cfg.py Widens physics_material type from RigidBodyMaterialCfg to RigidBodyMaterialBaseCfg to remove the hard isaaclab_physx dependency for kitless installs.
source/isaaclab/isaaclab/terrains/terrain_importer_cfg.py Same RigidBodyMaterialCfg → RigidBodyMaterialBaseCfg widening as simulation_cfg.py for kitless compatibility.

Sequence Diagram

sequenceDiagram
    participant GHA as GitHub Actions
    participant Changes as changes job
    participant UV as install-tests (uv)
    participant Conda as install-tests-conda
    participant Runner as run_install_ci.py
    participant Docker as Docker

    GHA->>Changes: trigger (always)
    Changes-->>GHA: "run_install_tests=true (always)"

    GHA->>UV: start (needs: changes)
    UV->>Runner: "docker --gpu -- --tb=short -sv -m uv"
    Runner->>Docker: build Dockerfile.installci (uv image)
    Docker-->>Runner: uv_image_tag
    Runner->>Docker: "run uv image (timeout=5400s)"
    Docker-->>Runner: pytest results (uv tests only)
    Runner-->>UV: exit code

    GHA->>Conda: start (needs: changes)
    Conda->>Runner: "docker --conda --gpu -- --tb=short -sv -m conda"
    Runner->>Docker: build Dockerfile.installci (uv base)
    Docker-->>Runner: uv_image_tag
    Runner->>Docker: "build Dockerfile.installci-conda (UV_IMAGE=uv_image_tag)"
    Docker-->>Runner: conda_image_tag
    Runner->>Docker: "run conda image (timeout=7200s)"
    Docker-->>Runner: pytest results (conda tests only)
    Runner-->>Conda: exit code
Loading

Comments Outside Diff (2)

  1. .github/workflows/install-ci.yml, line 82-91 (link)

    P2 any_match() is now dead code

    The any_match() shell function was previously called to decide whether to run tests. After the refactor removed the decide() call and replaced it with summarize "$changed_files", any_match() is no longer invoked anywhere. It can be removed to keep the script clean.

  2. tools/run_install_ci.py, line 1121-1143 (link)

    P2 Unused BASE_IMAGE build-arg passed to the conda Dockerfile

    _build() always adds --build-arg BASE_IMAGE={args.base_image} to the docker build command. Dockerfile.installci-conda only declares ARG UV_IMAGE; it has no ARG BASE_IMAGE. Docker emits a [Warning] One or more build-args [...] were not consumed warning for every conda image build, which adds noise to CI logs and can obscure real build issues. Consider passing BASE_IMAGE only when building the uv image, or add ARG BASE_IMAGE to the conda Dockerfile (even if unused) to silence the warning explicitly.

Reviews (1): Last reviewed commit: "add changelog fragment" | Re-trigger Greptile

if: needs.changes.outputs.run_install_tests == 'true'
runs-on: [self-hosted, gpu]
timeout-minutes: 90
timeout-minutes: 120
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Conda job timeout leaves no headroom for Docker builds

Both install-tests and install-tests-conda are set to timeout-minutes: 120. For the conda job, run_install_ci.py sets an inner Docker run timeout of 7200 s (exactly 120 minutes). Every second spent on actions/checkout, building the uv base image, and then building the conda image on top of it eats into that same 120-minute budget — meaning the actual Docker run will be cancelled by GitHub Actions before the inner timeout ever fires. In practice even a fast build (~5–10 minutes) leaves the Docker run with only ~110 minutes instead of 120, and the GitHub job is the one that kills the container without a clean JUnit report. Consider raising this to at least timeout-minutes: 150 (or 160) to provide a genuine buffer for build overhead.

Comment on lines +326 to +329
# Prepend the two internal registries so they are tried before pypi.org.
for url in _INTERNAL_ISAACSIM_INDEX_URLS:
if url not in extra_index_urls:
extra_index_urls.insert(0, url)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 insert(0, url) loop reverses the intended order of internal registries

The loop inserts each URL from _INTERNAL_ISAACSIM_INDEX_URLS at position 0 one at a time. After both inserts the list is [ct-omniverse-pypi, sw-isaacsim-pypi, NVIDIA_INDEX_URL, ...] — the opposite of the declaration order in _INTERNAL_ISAACSIM_INDEX_URLS (where sw-isaacsim-pypi is listed first). With uv's unsafe-best-match strategy, index ordering can affect resolution. Replace the loop with a slice-prepend to preserve the declared order.

Suggested change
# Prepend the two internal registries so they are tried before pypi.org.
for url in _INTERNAL_ISAACSIM_INDEX_URLS:
if url not in extra_index_urls:
extra_index_urls.insert(0, url)
# Prepend the two internal registries so they are tried before pypi.org.
prepend = [url for url in _INTERNAL_ISAACSIM_INDEX_URLS if url not in extra_index_urls]
extra_index_urls = prepend + extra_index_urls

Copy link
Copy Markdown

@isaaclab-review-bot isaaclab-review-bot Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Isaac Lab Review Bot

Summary

This PR adds automated installation workflow tests for conda/uv/modularized paths (7 test combinations), a conda-enabled Docker image, env var overrides for internal Isaac Sim builds in the install CLI, and makes PhysX/OvPhysX imports optional in the cartpole task config to support kitless (Newton-only) installs. It also changes SimulationCfg.physics_material and TerrainImporterCfg.physics_material from RigidBodyMaterialCfg to RigidBodyMaterialBaseCfg for solver-agnostic defaults.

Design Assessment

Acceptable, but the conditional import pattern needs consistency. The RigidBodyMaterialBaseCfg refactor is clean — it's the correct solver-agnostic abstraction and backward-compatible via the existing __getattr__ lazy-forwarding shim. The module-level class mutation pattern (CartpolePhysicsCfg.physx = PhysxCfg()) works correctly with the preset system's _preset_fields() which explicitly scans vars(cls). However, only cartpole_env_cfg.py gets the conditional import treatment — other task configs (e.g. humanoid) still have unconditional PhysX imports and will break on kitless installs.

Findings

Details in inline comments.

Test Coverage

  • New code (install workflow tests): Yes — 7 E2E combinations covering uv/conda × kitless/pip/source
  • install.py env var logic: No — complex URL-building and version-spec parsing has no unit tests
  • cartpole_env_cfg.py fallback: Implicit only — kitless E2E tests exercise the happy path but no negative-path tests for requesting unavailable presets
  • Type change (RigidBodyMaterialBaseCfg): No dedicated test — backward-compatible via inheritance, low risk

CI Status

  • Build Wheel: ✅ pass
  • Check changelog fragments: ❌ fail
  • Installation Tests (uv): ⏳ pending
  • Installation Tests (conda): ⏳ pending
  • pre-commit: ⏳ pending

Verdict

Minor fixes needed

The overall approach is sound — modularized install support is valuable, and the test matrix is comprehensive. The RigidBodyMaterialBaseCfg change is clean. Address the warning-level items (asymmetric import guarding, URL insertion order) and consider adding unit tests for _install_isaacsim() env var logic. The changelog fragment CI failure also needs attention.

from isaaclab_ovphysx.physics import OvPhysxCfg
from isaaclab_physx.physics import PhysxCfg

try:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Warning: Asymmetric import guardingisaaclab_newton (line 8) is imported unconditionally while isaaclab_ovphysx and isaaclab_physx are guarded with try/except.

If the goal is to support modularized installs, this means an install with PhysX-only (no Newton) would crash on import of the cartpole env. If Newton is intentionally a hard dependency for all kitless installs, a brief comment documenting that assumption would help future maintainers.

Also worth noting: this is the only task config with conditional imports — other envs (humanoid, etc.) still import isaaclab_physx unconditionally and will fail on kitless installs.

use_cuda_graph=True,
)
ovphysx: OvPhysxCfg = OvPhysxCfg()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Warning: Preset names silently vanish when backends are unavailable — When PhysxCfg is None, CartpolePhysicsCfg.physx is never created, and when OvPhysxCfg is None, .ovphysx is never created. A user requesting presets=physx on a kitless install gets a generic "Unknown preset 'physx'" error from the preset system rather than a clear "isaaclab_physx is not installed" message.

This works correctly (not a bug), but the error UX could be improved. Consider adding a comment or, better, a descriptive error path in the preset resolver for known-but-unavailable backends.

version_spec = os.environ.get("ISAACSIM_VERSION_SPEC", ISAACSIM_VERSION_SPEC)
extras = os.environ.get("ISAACSIM_EXTRAS", ISAACSIM_EXTRAS)
use_pre = os.environ.get("ISAACSIM_USE_PRE", "").strip().lower() in ("1", "true", "yes")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Suggestion: insert(0, ...) in a loop reverses the declaration order — Iterating _INTERNAL_ISAACSIM_INDEX_URLS and calling insert(0, url) each time produces [ct-omniverse, sw-isaacsim, NVIDIA] instead of [sw-isaacsim, ct-omniverse, NVIDIA].

Practically this doesn't matter (pip resolves across all indexes regardless of --extra-index-url order), but if declaration order was intentional, replace the loop with:

Suggested change
# Prepend the two internal registries so they are tried before pypi.org.
for url in reversed(_INTERNAL_ISAACSIM_INDEX_URLS):
if url not in extra_index_urls:
extra_index_urls.insert(0, url)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

infrastructure isaac-lab Related to Isaac Lab team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant